﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using ITAP.ContourBuilder.Constraints;
using ITAP.Core.Modelling.Primitives;
using System.Drawing;
using ITAP.Core.Modelling;

namespace ITAP.ContourBuilder.Components
{
    [Serializable]
    public class ContourModel : IContourComponent, IRotateable, IEnumerable<ContourElement>
    {
        public bool Modified
        {
            get { return false; }
            set { }
        }

        private bool isPrimitiveDependsOnDisplayParameters = false;

        public bool IsPrimitiveDependsOnDisplayParameters
        {
            get { return isPrimitiveDependsOnDisplayParameters; }
        }


        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }



        public ContourModel()
        {
            this.interiorRings = new List<IContourComponent>();
            this.innerComponents = new List<IContourComponent>();
            this.preUpdatePriority = new List<Type>();
            this.postUpdatePriority = new List<Type>();
            this.connectedComponents = new List<IContourComponent>();
        }

        public int ContourElementsCount
        {
            get
            {
                int counter = 0;
                foreach (IContourComponent innerComponent in this.innerComponents)
                    if (innerComponent is ContourElement)
                        counter++;

                return counter;
            }
        }

        private List<Type> preUpdatePriority;

        public List<Type> PreUpdatePriority
        {
            get { return preUpdatePriority; }
            set { preUpdatePriority = value; }
        }
        private List<Type> postUpdatePriority;

        public List<Type> PostUpdatePriority
        {
            get { return postUpdatePriority; }
            set { postUpdatePriority = value; }
        }

        private List<IContourComponent> interiorRings;

        public IEnumerable<IContourComponent> InteriorRings
        {
            get { return interiorRings; }
        }

        public void AddInteriorRing(IContourComponent ring)
        {
            this.interiorRings.Add(ring);
            this.AddInnerComponent(ring);
        }

        public void RemoveInteriorRing(IContourComponent ring)
        {       
            this.interiorRings.Remove(ring);
            this.RemoveInnerComponent(ring);
        }


        #region IContourComponent Members

        private List<IContourComponent> connectedComponents;
        public IEnumerable<IContourComponent> ConnectedComponents
        {
            get { return connectedComponents; }
        }

        private List<IContourComponent> innerComponents;
        public IEnumerable<IContourComponent> InnerComponents
        {
            get { return innerComponents; }
        }

        //private List<IContourConstraint> constraints;
        public IEnumerable<IContourConstraint> Constraints
        {
            get { throw new NotImplementedException(); }
        }

        private List<IContourComponent> componentsInTolerance = new List<IContourComponent>();
        public ICollection<IContourComponent> ComponentsInTolerance
        {
            get { return componentsInTolerance; }
        }

        //private List<Type> acceptedTypesToConnect;
        public ICollection<Type> AcceptedTypesToConnect
        {
            get { throw new NotImplementedException(); }
        }

        public IPrimitive Primitive
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public RectangleF BoundingBox
        {
            get
            {
                IEnumerable<IContourComponent> allComponents = this.GetAllInnerComponents();
                int counter = 0;
                RectangleF bbox = RectangleF.Empty;
                foreach (IContourComponent innerComponent in allComponents)
                {
                    if (counter == 0)
                    {
                        if (innerComponent.IsComponentDotted)
                            bbox = new RectangleF((float)innerComponent.Position.X, (float)innerComponent.Position.Y, 0, 0);
                        else
                            bbox = innerComponent.BoundingBox;

                        counter++;
                        continue;
                    }
                    RectangleF innerComponentBbox;
                    if (innerComponent.IsComponentDotted)
                            innerComponentBbox = new RectangleF((float)innerComponent.Position.X, (float)innerComponent.Position.Y, 0, 0);
                        else
                            innerComponentBbox = innerComponent.BoundingBox;

                    bbox = RectangleF.Union(bbox, innerComponentBbox);
                    counter++;
                }
                return bbox;
            }
        }

        public void Update()
        {
            IEnumerable<IContourComponent> allInnerComponents = this.GetAllInnerComponents();
            foreach (Type type in this.preUpdatePriority)
            {
                foreach (IContourComponent innerComponent in allInnerComponents)
                    if (innerComponent.GetType() == type)
                        innerComponent.Update();
            }

            foreach (Type type in this.postUpdatePriority)
            {
                foreach (IContourComponent innerComponent in allInnerComponents)
                    if (innerComponent.GetType() == type)
                        innerComponent.Update();
            }
        }

        public IContourComponent ConnectTo(IContourComponent component)
        {
            throw new NotImplementedException();
        }

        public IContourComponent ConnectToEnded(IEndedContourComponent component, EndType endToConnect)
        {
            throw new NotImplementedException();
        }

        public IContourComponent DisconnectFrom(IContourComponent component)
        {
            throw new NotImplementedException();
        }

        private IContourComponent parent;
        public IContourComponent Parent
        {
            get
            {
                return parent;
            }
            set
            {
                parent = value;
            }
        }

        private int serialNumber;
        public int SerialNumber
        {
            get
            {
                return serialNumber;
            }
            set
            {
                serialNumber = value;
            }
        }

        public ModelPoint Position
        {
            get { throw new NotImplementedException(); }
        }

        public void MoveTo(ModelPoint destination)
        {
            throw new NotImplementedException();
        }

        public void MoveWithOffset(ModelVector offset)
        {
            foreach (IContourComponent innerComponent in this.innerComponents)
                innerComponent.MoveWithOffset(offset);
        }

        public bool EnableConstraints
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        private bool selected = false;
        public bool Selected
        {
            get
            {
                return false;
                return selected;
            }
            set
            {
                return;
                selected = value;
            }
        }

        public void SelectByPoint(ModelPoint point)
        {            
            //throw new NotImplementedException();
        }

        public double Tolerance
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region IRotateable Members

        public void Rotate(Angle angle, ModelPoint rotateCenter)
        {
            foreach (IContourComponent innerComponent in this.innerComponents)
                if (innerComponent is IRotateable)
                    (innerComponent as IRotateable).Rotate(angle, rotateCenter);            
        }

        #endregion

        #region IContourComponent Members


        public void AcceptVisitor(IContourVisitor visitor)
        {
            visitor.Visit(this);
        }

        #endregion

        #region IContourVisitor Members

        public void Visit(ContourModel model)
        {
            //throw new NotImplementedException();
        }

        public void Visit(ContourContainer container)
        {
            //throw new NotImplementedException();
        }

        public void Visit(ContourElement element)
        {
            //throw new NotImplementedException();
        }

        public void Visit(ContourConnector connector)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IEnumerable<ContourElement> Members

        public IEnumerator<ContourElement> GetEnumerator()
        {
            ContourElementEnumerator enumerator = new ContourElementEnumerator(this);
            return enumerator;
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IContourComponent Members


        public void SetDisplayParameters(int screenWidth, int screenHeight, RectangleF modelRectangle, ITAP.Core.Calculations.ModelCoordinateConverter coordinateConverter, ModelPoint cameraCenter)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IContourComponent Members


        public bool IsInTolerance(ModelPoint point, double tolerance)
        {
            return false;
        }

        #endregion

        #region IContourComponent Members


        public bool IsComponentDotted
        {
            get { return false; }
        }

        #endregion

        #region IContourComponent Members


        public bool CanSelectByPoint(ModelPoint point)
        {
            return false;
        }

        #endregion

        #region IContourVisitor Members


        public void Visit(ContourElementHook hook)
        {
//            throw new NotImplementedException();
        }

        #endregion

        #region IContourVisitor Members


        public void Visit(ContourText text)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IContourVisitor Members


        public void Visit(ContourCommentLine commentLine)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IContourVisitor Members


        public void Visit(ITAP.ContourBuilder.Components.Dimensions.Dimension dimension)
        {
           // throw new NotImplementedException();
        }

        #endregion

        #region IContourVisitor Members


        public void Visit(ContourHole hole)
        {
           // throw new NotImplementedException();
        }

        #endregion

        #region IContourComponent Members


        public int Id
        {
            get
            {
                return -1;
            }
            set
            {
            }
        }

        #endregion

        #region IContourComponent Members


        public bool HasBoundingBox
        {
            get
            {
                return true;
            }
            set
            {
            }
        }

        #endregion
    }

    public class ContourElementEnumerator : IEnumerator<ContourElement>
    {
        List<ContourElement> elements;
        ContourElement firstElement = null;
        int currentIndex = -1;
        public ContourElementEnumerator(ContourModel model)
        {
            this.elements = new List<ContourElement>();
            foreach(IContourComponent innerComponent in model.InnerComponents)
                if(innerComponent is ContourElement)                
                    if(firstElement == null)
                    {
                        this.firstElement = innerComponent as ContourElement;
                        break;
                    }

            elements.Add(firstElement);
            ContourElement currentElement = this.GetNextContourElement(firstElement);

            while(currentElement != this.firstElement)
            {
                elements.Add(currentElement);
                currentElement = this.GetNextContourElement(currentElement);                
            }
        }

        private ContourElement GetNextContourElement(ContourElement element)
        {
            if (element.EndConnectedComponent == null)
                throw new ContourBuilderException("Невозможно получить следующий элемент замкнутого контура. Возможно, контур не замкнут.");
            if (!(element.EndConnectedComponent is ContourConnector))
                throw new ContourBuilderException("Получение следующего элемента невозможно, т.к. на конце отсутствует коннектор.");
            foreach (IContourComponent component in element.EndConnectedComponent.ConnectedComponents)
            {
                if (component != element && component is ContourElement)
                    return component as ContourElement;
            }
            throw new ContourBuilderException("Контур не замкнут.");
        }

        #region IEnumerator<ContourElement> Members

        public ContourElement Current
        {
            get 
            {
                if (currentIndex == -1)
                    return null;
                else
                    return this.elements[currentIndex]; 
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            elements = null;
        }

        #endregion

        #region IEnumerator Members

        object System.Collections.IEnumerator.Current
        {
            get { return this.elements[currentIndex]; }
        }

        public bool MoveNext()
        {
            if (currentIndex < this.elements.Count - 1)
            {
                currentIndex++;
                return true;
            }
            else if(currentIndex == this.elements.Count - 1)
            {
                currentIndex = -1;
                return false;
            }
            return false;
        }

        public void Reset()
        {
            currentIndex = -1;
        }

        #endregion
    }
}
